package com.dx.VegetableOxygenBarBackEnd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dx.VegetableOxygenBarBackEnd.common.ErrorCode;
import com.dx.VegetableOxygenBarBackEnd.exception.BusinessException;
import com.dx.VegetableOxygenBarBackEnd.mapper.TeamMapper;
import com.dx.VegetableOxygenBarBackEnd.mapper.UserMapper;
import com.dx.VegetableOxygenBarBackEnd.model.domain.dto.TeamQuery;
import com.dx.VegetableOxygenBarBackEnd.model.domain.entity.Team;
import com.dx.VegetableOxygenBarBackEnd.model.domain.entity.User;
import com.dx.VegetableOxygenBarBackEnd.model.domain.entity.UserTeam;
import com.dx.VegetableOxygenBarBackEnd.model.domain.request.TeamJoinRequest;
import com.dx.VegetableOxygenBarBackEnd.model.domain.request.TeamQuitRequest;
import com.dx.VegetableOxygenBarBackEnd.model.domain.request.TeamUpdateRequest;
import com.dx.VegetableOxygenBarBackEnd.model.domain.vo.TeamUserVo;
import com.dx.VegetableOxygenBarBackEnd.model.domain.vo.UserVo;
import com.dx.VegetableOxygenBarBackEnd.model.enums.TeamStatusEnum;
import com.dx.VegetableOxygenBarBackEnd.service.TeamService;
import com.dx.VegetableOxygenBarBackEnd.service.UserService;
import com.dx.VegetableOxygenBarBackEnd.service.UserTeamService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author dengxiao
 * @description 针对表【team(队伍)】的数据库操作Service实现
 * @createDate 2024-08-29 10:54:46
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
        implements TeamService {

    @Resource
    private UserTeamService userTeamService;

    @Resource
    private UserService userService;
    @Resource
    private UserMapper userMapper;
    @Resource
    private RedissonClient redissonClient;


    /**
     * 增加队伍
     *
     * @param team      新增队伍参数
     * @param loginUser 当前登录用户
     * @return 新增队伍id
     */
    @Override
    @Transactional
    public long addTeam(Team team, User loginUser) {
        final long userId = loginUser.getId();
        //1.请求参数是否为空?
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //2.是否登录，未登录不允许创建
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        //3. 校验信息
        //i.队伍人数>1日<= 20
        int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(1);
        if (maxNum < 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数设置错误");
        }
        //ii.队伍标题<=20
        String teamName = team.getTeamName();
        if (teamName.length() > 20 || StringUtils.isBlank(teamName)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍名称设置错误");
        }
        //iii. 描述<= 512
        String description = team.getDescription();
        if (description.length() > 512 && StringUtils.isNotBlank(description)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述设置错误");
        }
        //iv. status 是否公开(int)不传默认为 0(公开)
        int status = Optional.ofNullable(team.getTeamState()).orElse(0);
        TeamStatusEnum value = TeamStatusEnum.getEnumByValue(status);//获取队伍text状态而不是int
        if (value == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态设置错误");
        }
        //v.如果 status 是加密状态，一定要有密码，且密码<=32
        String teamPassword = team.getTeamPassword();
        //TeamStatusEnum.SECRET.equals(value)一个是判断字符串相同TeamStatusEnum.SECRET.equalsValue(status)一个是判断int相同
        if (TeamStatusEnum.SECRET.equalsValue(status)) {
            if (teamPassword.length() > 32 || StringUtils.isBlank(teamPassword)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍密码设置错误");
            }
        }
        //vi. 超时时间 >当前时间
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间 > 当前时间");
        }
        //vii. 校验用户最多创建5 个队伍
        //todo this code has bug
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long count = this.count(queryWrapper);
        if (count >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建5个用户");
        }
        //4.插入队伍信息到队伍表
        team.setUserId(userId);
        team.setId(null);
        boolean res = this.save(team);
        if (!res || team.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        //5.插入用户 =>队伍关系到关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setTeamId(team.getId());
        userTeam.setUserId(userId);
        userTeam.setJoinTime(new Date());
        boolean save = userTeamService.save(userTeam);
        if (!save) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }

        return team.getId();
    }
    //todo 关联查询已经加入队伍的用户

    /**
     * 查询已经在当前队伍的用户
     *
     * @param teamId 当前队伍id
     * @return 用户列表
     */
    @Override
    public List<UserVo> listUserInTeam(long teamId) {
        if (teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        this.getTeamById(teamId);
        List<User> users = userMapper.selectUsersByTeamId(teamId);
        List<UserVo> userVoList = new ArrayList<>();
        for (User user : users) {
            UserVo userVo = new UserVo();
            BeanUtils.copyProperties(user, userVo);
            userVoList.add(userVo);
        }
        return userVoList;
    }

    /**
     * 查询队伍列表
     *
     * @param teamQuery 查询列表参数
     * @return 队伍用户信息
     */
    @Override
    public List<TeamUserVo> listTeam(TeamQuery teamQuery, Boolean isAdmin) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        //组合查询条件
        if (teamQuery != null) {
            Long id = teamQuery.getId();
            if (id != null && id > 0) {
                queryWrapper.eq("id", id);
            }
            List<Long> idList = teamQuery.getIdList();
            if (!CollectionUtils.isEmpty(idList)) {
                queryWrapper.in("id", idList);
            }
            //根据队伍名称查询
            String teamName = teamQuery.getTeamName();
            if (StringUtils.isNotBlank(teamName)) {
                queryWrapper.like("teamName", teamName);
            }
            //根据队伍描述查询
            String description = teamQuery.getDescription();
            if (StringUtils.isNotBlank(description)) {
                queryWrapper.like("description", description);
            }
            String searchText = teamQuery.getSearchText();
            if (StringUtils.isNotBlank(searchText)) {
                queryWrapper
                        .and(qw -> qw
                                .like("teamName", searchText)
                                .or()
                                .like("description", searchText));
            }

            //根据创建人查询
            Long userId = teamQuery.getUserId();
            if (userId != null && userId > 0) {
                queryWrapper.eq("userId", userId);
            }

            // 根据队伍状态查询，只有管理员才可以查看加密的队伍
            Integer teamState = teamQuery.getTeamState();
            TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(teamState);
            if (statusEnum == null) {
                // 如果 statusEnum 为空，则查询 PUBLIC, SECRET 和 PRIVATE 的队伍
                queryWrapper.in("teamState", TeamStatusEnum.PUBLIC.getValue(),
                        TeamStatusEnum.SECRET.getValue(),
                        TeamStatusEnum.PRIVATE.getValue());
            } else {
                // 如果不是管理员且队伍状态为 PRIVATE，则抛出异常
                if (!isAdmin && statusEnum.equals(TeamStatusEnum.PRIVATE)) {
                    throw new BusinessException(ErrorCode.NO_AUTH);
                }
                queryWrapper.eq("teamState", statusEnum.getValue());
            }

            //根据队伍最大人数查询
            Integer maxNum = teamQuery.getMaxNum();
            if (maxNum != null && maxNum > 0) {
                queryWrapper.eq("maxNum", maxNum);
            }
        }
        //不展示已经过期的队伍  过期时间为空 或者 过期时间大于当前时间展示
        queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> teamList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
        //关联用户查询信息
        List<TeamUserVo> teamUserVoList = new ArrayList<>();
        for (Team team : teamList) {
            Long userId = team.getUserId();
            if (userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            TeamUserVo teamUserVo = new TeamUserVo();
            if (user != null) {
                UserVo userVo = new UserVo();
                BeanUtils.copyProperties(user, userVo);
                BeanUtils.copyProperties(team, teamUserVo);
                teamUserVo.setUserVoList(userVo);
            }
            teamUserVoList.add(teamUserVo);
        }

        return teamUserVoList;
    }


    /**
     * 更新队伍
     *
     * @param teamUpdateRequest
     * @param loginUser
     * @return
     */
    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        // 参数校验
        if (teamUpdateRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long teamId = teamUpdateRequest.getId();
        if (teamId == null || teamId < 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }

        // 获取旧的队伍信息
        Team oldTeam = this.getTeamById(teamId);
        // 权限校验
        if (!oldTeam.getUserId().equals(loginUser.getId()) && !userService.isAdminUser(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        boolean hasChanged = false;
        // 判断传入的新值和老值是否相同，相同则不做操作
        String newTeamName = teamUpdateRequest.getTeamName();
        if (newTeamName != null) {
            if (StringUtils.isBlank(newTeamName) || newTeamName.length() > 20) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍名称设置错误");
            }
            if (!newTeamName.equals(oldTeam.getTeamName())) {
                oldTeam.setTeamName(newTeamName);
                hasChanged = true;
            }
        }
        String newDescription = teamUpdateRequest.getDescription();
        if (newDescription != null) {
            if (StringUtils.isNotBlank(newDescription) && newDescription.length() > 512) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述设置错误");
            }
            if (!newDescription.equals(oldTeam.getDescription())) {
                oldTeam.setDescription(newDescription);
                hasChanged = true;
            }
        }

        Integer newTeamState = teamUpdateRequest.getTeamState();
        if (newTeamState != null) {
            TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(newTeamState);
            if (teamStatusEnum == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "无效的队伍状态值");
            }

            if (!newTeamState.equals(oldTeam.getTeamState())) {
                oldTeam.setTeamState(newTeamState);
                hasChanged = true;
            }
            // 如果设置为 SECRET(2)，则需要检查密码
            if (teamStatusEnum == TeamStatusEnum.SECRET) {
                String newTeamPassword = teamUpdateRequest.getTeamPassword();
                if (newTeamPassword == null || newTeamPassword.isEmpty()) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密状态下必须设置密码");
                }
                if (newTeamPassword.length() > 32) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度不能超过32个字符");
                }
                if (!newTeamPassword.equals(oldTeam.getTeamPassword())) {
                    oldTeam.setTeamPassword(newTeamPassword);
                    hasChanged = true;
                }
            }
            if (teamStatusEnum != TeamStatusEnum.SECRET) {
                oldTeam.setTeamPassword("");
            }
        }
        Date newExpireTime = teamUpdateRequest.getExpireTime();
        if (newExpireTime != null) {
            if (new Date().after(newExpireTime)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "过期时间不能小于当前时间");
            }
            if (!newExpireTime.equals(oldTeam.getExpireTime())) {
                oldTeam.setExpireTime(newExpireTime);
                hasChanged = true;
            }
        }
        Integer newMaxNum = teamUpdateRequest.getMaxNum();
        if (newMaxNum != null) {
            if (newMaxNum < 1 || newMaxNum > 20) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数设置错误");
            }
            if (!newMaxNum.equals(oldTeam.getMaxNum())) {
                oldTeam.setMaxNum(newMaxNum);
                hasChanged = true;
            }
        }
        // 如果没有任何变化，直接返回 true
        if (!hasChanged) {
            return true;
        }
        // 执行更新操作，使用 oldTeam，因为它只包含更改的字段
        boolean res = this.updateById(oldTeam);
        return res;
    }

    /**
     * 加入队伍
     *
     * @param teamJoinRequest
     * @param loginUser
     * @return
     */
    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        //请求参数校验
        if (teamJoinRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long teamId = teamJoinRequest.getId();
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //查询的队伍是否存在校验
        Team team = this.getTeamById(teamId);
        //队伍过期时间校验
        if (team.getExpireTime() != null && team.getExpireTime().before(new Date())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已经过期");
        }
        //队伍状态校验
        Integer teamState = team.getTeamState();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(teamState);
        if (TeamStatusEnum.PRIVATE.equals(teamStatusEnum)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "禁止加入私密队伍");
        }
        //密码校验
        String teamPassword = teamJoinRequest.getTeamPassword();
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
            if (StringUtils.isBlank(teamPassword) || !team.getTeamPassword().equals(teamPassword)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
            }
        }
        //加锁，防止出现一起触发
        //加两把锁，一个锁队伍，只能同时加入一个，，，一个锁用户，

        //分布式锁
        RLock lock = redissonClient.getLock("shucai:join_team");
        try {
            while (true) {
                if(lock.tryLock(0, -1, TimeUnit.MILLISECONDS)){
                    System.out.println("getLock:" + Thread.currentThread().getId());
                    Long loginUserId = loginUser.getId();
                    //获取id的字符串的常量对象
//        synchronized (String.valueOf(loginUserId).intern()) {
                    QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("userId", loginUserId);
                    long hasJoinNum = userTeamService.count(queryWrapper);
                    if (hasJoinNum > 5) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "加入或创建的队伍大于5");
                    }
                    //判断用户是否重复加入队伍
                    queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("userId", loginUserId);
                    queryWrapper.eq("teamId", teamId);
                    long hasUserJoinTeam = userTeamService.count(queryWrapper);
                    if (hasUserJoinTeam > 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "重复加入队伍");
                    }
                    //判断队伍已经加入了多少用户
                    long teamHasJoinNum = this.getTeamUserNumByTeamId(teamId);
                    if (teamHasJoinNum >= team.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已经满了");
                    }
                    //修改用户队伍表信息
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(loginUserId);
                    userTeam.setTeamId(teamId);
                    userTeam.setJoinTime(new Date());
                    boolean res = userTeamService.save(userTeam);
                    return res;
                }

            }
        } catch (InterruptedException e) {
            log.error("redisson error", e);
            return false;
        } finally {
            //只能释放自己的锁
            if(lock.isHeldByCurrentThread()){
                System.out.println("unLock:"+Thread.currentThread().getId());
                lock.unlock();
            }

        }
        //判断用户已经加入的队伍数量     this是大锁，全部只能一个线程走    synchronized这里是单机锁
//        synchronized (this) {
//
//        }

    }

    /**
     * 退出队伍
     *
     * @param teamQuitRequest
     * @param loginUser
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        //传入参数校验
        if (teamQuitRequest == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        //队伍是否存在校验
        Long teamId = teamQuitRequest.getId();
        Team team = getTeamById(teamId);
        //检验用户是否在队伍中，只有在队伍中才可以退出
        Long userId = loginUser.getId();
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        queryWrapper.eq("userId", userId);
        long isExist = userTeamService.count(queryWrapper);
        if (isExist < 1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "未加入该队伍");
        }
        //判断队伍有多少人
        long teamHasJoinNum = this.getTeamUserNumByTeamId(teamId);
        //队伍只有一人，队伍解散
        if (teamHasJoinNum == 1) {
            //删除队伍
            this.removeById(teamId);
        }//队伍有多人
        else {
            //是否    是队长
            if (team.getUserId().equals(userId)) {
                //队伍转移给最早加入的用户
                QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                userTeamQueryWrapper.eq("teamId", teamId);
                userTeamQueryWrapper.last("order by id asc");
                List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
                if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                UserTeam nextUserTeam = userTeamList.get(1);
                //更新队伍队长
                Long nextUserId = nextUserTeam.getUserId();
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                updateTeam.setUserId(nextUserId);
                boolean res = this.updateById(updateTeam);
                if (!res) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队长失败");
                }

            }


        }
        //移除当前用户和加入队伍的关系
        return userTeamService.remove(queryWrapper);
    }

    /**
     * 根据teamId获取队伍信息
     *
     * @param teamId
     * @return
     */
    private Team getTeamById(Long teamId) {
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        return team;
    }

    /**
     * 解散队伍
     *
     * @param teamId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteTeam(long teamId, User loginUser) {
        //校验参数
        if (teamId <= 0) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //校验队伍存在？
        Team team = this.getTeamById(teamId);
        //校验是不是队长？
        if (!team.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //移除所有加入队伍的关联信息
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId", teamId);
        boolean remove = userTeamService.remove(userTeamQueryWrapper);
        if (!remove) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除关联信息失败");
        }
        //删除队伍
        boolean res = this.removeById(teamId);
        return res;
    }

    /**
     * 统计加入队伍用户的人数
     *
     * @param teamId
     * @return
     */
    private long getTeamUserNumByTeamId(long teamId) {
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        long count = userTeamService.count(queryWrapper);
        return count;
    }

}




